My Downloads folder: paper_final_v3.pdf, document(1).pdf, invoice_2024.pdf. Every PDF has its metadata inside (author, date, title), but the filename tells you nothing, and the friction of opening a file, finding the relevant fields, and typing out a consistent name is just enough to ensure it never happens.

The tool runs Ollama with qwen3.5:4b locally. PyMuPDF extracts text from the document (PDF, EPUB, XPS, OpenXPS, CBZ, FB2), capped at 100,000 characters, and the LLM reads the text and returns a JSON object with the extracted fields. Academic papers get renamed to [year] [author] - [title].[ext]; invoices to [YYYYMMDD] [Institution] - [Title].[ext].

[model]
name = "qwen3.5:4b"
backend = "ollama"

[papers]
format = "{year} {author} - {title}"
prompt = "Extract: year, first author surname, title"

[invoices]
format = "{date} {institution} - {title}"
prompt = "Extract: date (YYYYMMDD), institution, description"

Metadata extraction is a natural language task: the title of a paper isn't always on the first line, and the date of an invoice might be in the header, the footer, or buried in a table. A 4B parameter model handles the ambiguity with high reliability.

Most of the development time went into file system edge cases. Filename sanitization for characters that Windows rejects (colons, question marks, quotation marks, all common in paper titles). Conflict resolution via counter suffix when multiple files resolve to the same name, which happens often with invoices from the same company. Idempotency: files that already have the correct date prefix get skipped, so running the tool twice doesn't double-rename anything. OCR'd documents with recognition errors are mostly handled by the model's tolerance for messy text, but heavily degraded scans with handwritten dates occasionally produce garbled output.

Everything is configurable via TOML (model selection, naming format, processing settings), so switching backends or adjusting patterns is a config change. New papers and invoices get processed in a batch. I scan filenames instead of opening each document.